package com.cino.interceptor;

import com.cino.entity.SysAuditLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.Field;
import java.util.Properties;

/**
 * 表名动态替换拦截器
 * 用于拦截审计日志插入操作，动态替换表名
 * 
 * 功能说明：
 * 1. 拦截针对AuditLogMapper的INSERT操作
 * 2. 从AuditLogEntity参数中获取自定义存储表名
 * 3. 只有设置了自定义表名时，才进行动态表名替换
 * 4. 动态替换SQL中的表名（audit_log → 自定义表名）
 * 5. 只影响审计日志的保存，不影响其他操作
 * 6. 线程安全：不依赖ThreadLocal，直接从实体参数获取表名信息
 */
@Intercepts({
    @Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = {java.sql.Connection.class, Integer.class}
    )
})
@Slf4j
public class TableNameInterceptor implements Interceptor {
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1. 获取StatementHandler
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        
        // 2. 获取MetaObject用于访问内部属性
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        
        // 3. 获取MappedStatement和BoundSql
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        
        // 4. 只拦截INSERT操作，且只针对AuditLogMapper
        if (mappedStatement.getSqlCommandType() == SqlCommandType.INSERT && isAuditLogOperation(mappedStatement)) {
            
            // 5. 获取参数对象
            Object parameterObject = boundSql.getParameterObject();
            String customStorageTable = null;
            
            if (parameterObject instanceof SysAuditLog) {
                SysAuditLog entity = (SysAuditLog) parameterObject;
                customStorageTable = entity.getStorageTable();
            }
            
            // 6. 只有在有自定义表名时才进行表名替换
            if (customStorageTable != null && !customStorageTable.trim().isEmpty()) {
                
                // 7. 获取原始SQL并替换表名
                String originalSql = boundSql.getSql();
                String newSql = originalSql.replaceAll("\\baudit_log\\b", customStorageTable);
                
                // 8. 通过反射修改BoundSql中的sql字段
                Field sqlField = BoundSql.class.getDeclaredField("sql");
                sqlField.setAccessible(true);
                sqlField.set(boundSql, newSql);
            }
        }
        
        // 9. 执行原操作
        return invocation.proceed();
    }
    
    /**
     * 判断是否是审计日志操作
     * 
     * @param ms MappedStatement
     * @return 如果是审计日志操作返回true
     */
    private boolean isAuditLogOperation(MappedStatement ms) {
        String statementId = ms.getId();
        return statementId.contains("AuditLogMapper") || statementId.contains("auditLog");
    }
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties) {
        // 无需设置额外属性
    }
}
